package cn.iocoder.yudao.module.ao.service.vocationalclassschedule;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;

import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.time.LocalDateTime;
import cn.iocoder.yudao.module.ao.controller.admin.vocationalclassschedule.vo.*;
import cn.iocoder.yudao.module.ao.dal.dataobject.vocationalclassschedule.VocationalClassScheduleDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.ao.dal.mysql.vocationalclassschedule.VocationalClassScheduleMapper;
import cn.iocoder.yudao.module.ao.service.academicyear.AcademicYearService;
import cn.iocoder.yudao.module.ao.service.teacher.TeacherService;
import cn.iocoder.yudao.module.ao.service.classlocation.ClassLocationService;
import cn.iocoder.yudao.module.ao.service.course.CourseService;
import cn.iocoder.yudao.module.ao.controller.admin.academicyear.vo.AcademicYearSaveReqVO;
import cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherSaveReqVO;
import cn.iocoder.yudao.module.ao.controller.admin.classlocation.vo.ClassLocationSaveReqVO;
import cn.iocoder.yudao.module.ao.controller.admin.course.vo.CourseSaveReqVO;
import cn.iocoder.yudao.module.ao.dal.dataobject.academicyear.AcademicYearDO;
import cn.iocoder.yudao.module.ao.dal.dataobject.teacher.TeacherDO;
import cn.iocoder.yudao.module.ao.dal.dataobject.classlocation.ClassLocationDO;
import cn.iocoder.yudao.module.ao.dal.dataobject.course.CourseDO;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;
import static cn.iocoder.yudao.module.ao.enums.ErrorCodeConstants.*;

/**
 * 中高技班级课 Service 实现类
 *
 * @author 禾安
 */
@Slf4j
@Service
@Validated
public class VocationalClassScheduleServiceImpl implements VocationalClassScheduleService {

    @Resource
    private VocationalClassScheduleMapper vocationalClassScheduleMapper;

    @Resource
    private AcademicYearService academicYearService;

    @Resource
    private TeacherService teacherService;

    @Resource
    private ClassLocationService classLocationService;

    @Resource
    private CourseService courseService;

    @Override
    public Long createVocationalClassSchedule(VocationalClassScheduleSaveReqVO createReqVO) {
        // 插入
        VocationalClassScheduleDO vocationalClassSchedule = BeanUtils.toBean(createReqVO, VocationalClassScheduleDO.class);
        vocationalClassScheduleMapper.insert(vocationalClassSchedule);

        // 返回
        return vocationalClassSchedule.getId();
    }

    @Override
    public void updateVocationalClassSchedule(VocationalClassScheduleSaveReqVO updateReqVO) {
        // 校验存在
        validateVocationalClassScheduleExists(updateReqVO.getId());
        // 更新
        VocationalClassScheduleDO updateObj = BeanUtils.toBean(updateReqVO, VocationalClassScheduleDO.class);
        vocationalClassScheduleMapper.updateById(updateObj);
    }

    @Override
    public void deleteVocationalClassSchedule(Long id) {
        // 校验存在
        validateVocationalClassScheduleExists(id);
        // 删除
        vocationalClassScheduleMapper.deleteById(id);
    }

    @Override
        public void deleteVocationalClassScheduleListByIds(List<Long> ids) {
        // 删除
        vocationalClassScheduleMapper.deleteByIds(ids);
        }


    private void validateVocationalClassScheduleExists(Long id) {
        if (vocationalClassScheduleMapper.selectById(id) == null) {
            throw exception(VOCATIONAL_CLASS_SCHEDULE_NOT_EXISTS);
        }
    }

    @Override
    public VocationalClassScheduleDO getVocationalClassSchedule(Long id) {
        return vocationalClassScheduleMapper.selectById(id);
    }

    @Override
    public PageResult<VocationalClassScheduleDO> getVocationalClassSchedulePage(VocationalClassSchedulePageReqVO pageReqVO) {
        return vocationalClassScheduleMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VocationalClassScheduleImportRespVO importVocationalClassScheduleList(List<VocationalClassScheduleImportExcelVO> importVocationalClassSchedules, Boolean updateSupport) {
        log.info("开始导入中高技班级课，解析到的数据条数: {}", importVocationalClassSchedules == null ? 0 : importVocationalClassSchedules.size());
        if (CollUtil.isEmpty(importVocationalClassSchedules)) {
            log.warn("导入的中高技班级课数据为空");
            throw exception(VOCATIONAL_CLASS_SCHEDULE_IMPORT_LIST_IS_EMPTY);
        }

        VocationalClassScheduleImportRespVO respVO = VocationalClassScheduleImportRespVO.builder()
                .createVocationalClassScheduleCount(0).updateVocationalClassScheduleCount(0).failureVocationalClassScheduleCount(0)
                .failureVocationalClassScheduleMessages(new ArrayList<>()).build();

        // 如果支持更新，先删除所有现有数据
        if (updateSupport) {
            log.info("支持更新模式：删除所有现有数据");
            vocationalClassScheduleMapper.delete(new LambdaQueryWrapperX<>());
            log.info("已删除所有现有的中高技班级课数据");
        }

        // 处理所有导入数据，不进行重复检查
        for (int i = 0; i < importVocationalClassSchedules.size(); i++) {
            VocationalClassScheduleImportExcelVO importVocationalClassSchedule = importVocationalClassSchedules.get(i);
            try {
                log.info("处理导入数据 第{}行: 学期={}, 班级={}, 节次={}",
                    i + 1, importVocationalClassSchedule.getTerm(), importVocationalClassSchedule.getClassName(), importVocationalClassSchedule.getClassSection());

                // 校验数据
                validateVocationalClassScheduleForImport(importVocationalClassSchedule);

                // 直接插入数据，不进行重复检查
                VocationalClassScheduleDO vocationalClassSchedule = BeanUtils.toBean(importVocationalClassSchedule, VocationalClassScheduleDO.class);
                vocationalClassScheduleMapper.insert(vocationalClassSchedule);
                respVO.setCreateVocationalClassScheduleCount(respVO.getCreateVocationalClassScheduleCount() + 1);

                log.info("成功插入第{}行数据", i + 1);

            } catch (Exception ex) {
                // 记录失败的数据
                respVO.setFailureVocationalClassScheduleCount(respVO.getFailureVocationalClassScheduleCount() + 1);
                respVO.getFailureVocationalClassScheduleMessages().add(String.format("第 %d 行：%s", i + 1, ex.getMessage()));
                log.error("第{}行数据插入失败: {}", i + 1, ex.getMessage());
            }
        }

        // 导入成功后，备份数据到相关管理模块
        if (respVO.getCreateVocationalClassScheduleCount() > 0) {
            log.info("开始备份导入数据到相关管理模块，成功导入{}条数据", respVO.getCreateVocationalClassScheduleCount());
            try {
                backupImportedDataToRelatedModules(importVocationalClassSchedules);
                log.info("数据备份完成");
            } catch (Exception e) {
                log.error("数据备份失败，但不影响主要导入流程", e);
            }
        } else {
            log.warn("没有成功导入的数据，跳过备份流程。创建数量: {}, 失败数量: {}",
                respVO.getCreateVocationalClassScheduleCount(), respVO.getFailureVocationalClassScheduleCount());
        }

        return respVO;
    }

    /**
     * 校验导入的中高技班级课数据
     */
    private void validateVocationalClassScheduleForImport(VocationalClassScheduleImportExcelVO importVocationalClassSchedule) {
        if (StrUtil.isBlank(importVocationalClassSchedule.getTerm())) {
            throw new IllegalArgumentException("学期信息不能为空");
        }
        if (StrUtil.isBlank(importVocationalClassSchedule.getClassName())) {
            throw new IllegalArgumentException("班级名称不能为空");
        }
        if (StrUtil.isBlank(importVocationalClassSchedule.getClassSection())) {
            throw new IllegalArgumentException("节次不能为空");
        }
    }

    /**
     * 备份导入数据到相关管理模块
     */
    private void backupImportedDataToRelatedModules(List<VocationalClassScheduleImportExcelVO> importData) {
        Set<String> processedAcademicYears = new HashSet<>();
        Set<String> processedTeachers = new HashSet<>();
        Set<String> processedLocations = new HashSet<>();
        Set<String> processedCourses = new HashSet<>();

        for (VocationalClassScheduleImportExcelVO data : importData) {
            try {
                // 1. 备份学年及班级信息（基于基本信息）
                backupAcademicYearData(data, processedAcademicYears);

                // 2. 备份教师信息（从班主任和课程信息中提取）
                backupTeacherDataFromAllSources(data, processedTeachers);

                // 3. 备份场地信息（从教室和课程信息中提取，智能识别场地类型）
                backupLocationDataFromAllSources(data, processedLocations);

                // 4. 备份课程信息（从课程表中提取所有科目）
                backupCourseDataFromAllSources(data, processedCourses);

            } catch (Exception e) {
                log.warn("备份单条数据失败: 班级={}, 教师={}, 教室={}, 错误={}",
                        data.getClassName(), data.getClassTeacher(), data.getClassroom(), e.getMessage());
            }
        }

        log.info("数据备份统计: 学年班级{}个, 教师{}个, 场地{}个, 课程{}个",
                processedAcademicYears.size(), processedTeachers.size(), processedLocations.size(), processedCourses.size());
    }

    /**
     * 备份学年及班级信息（基于中高技班级课管理模块的基本信息）
     */
    private void backupAcademicYearData(VocationalClassScheduleImportExcelVO data, Set<String> processedAcademicYears) {
        if (StrUtil.isBlank(data.getTerm()) || StrUtil.isBlank(data.getClassName())) {
            return;
        }

        // 解析学年信息
        String schoolYear = extractSchoolYear(data.getTerm());
        String term = extractTerm(data.getTerm());

        // 使用班级名称作为唯一标识，因为一个班级在一个学期中是唯一的
        String key = schoolYear + "-" + term + "-" + data.getClassName();
        if (processedAcademicYears.contains(key)) {
            return; // 已处理过
        }

        // 检查是否已存在相同的学年学期信息
        cn.iocoder.yudao.module.ao.controller.admin.academicyear.vo.AcademicYearPageReqVO pageReq =
                new cn.iocoder.yudao.module.ao.controller.admin.academicyear.vo.AcademicYearPageReqVO();
        pageReq.setSchoolYear(schoolYear);
        pageReq.setTermName(term);
        pageReq.setSchoolId(1L); // 默认学校ID，可根据实际情况调整
        pageReq.setPageSize(1);

        if (academicYearService.getAcademicYearPage(pageReq).getList().isEmpty()) {
            // 创建学年学期信息，基于中高技班级课管理模块的基本信息
            AcademicYearSaveReqVO academicYearReq = new AcademicYearSaveReqVO();
            academicYearReq.setSchoolId(1L); // 默认学校ID，可根据实际情况调整
            academicYearReq.setSchoolYear(schoolYear);
            academicYearReq.setTermName(term);
            academicYearReq.setTermCode(generateTermCode(term)); // 生成学期代码

            // 设置默认的开始和结束日期（可根据实际情况调整）
            academicYearReq.setStartDate(java.time.LocalDate.now());
            academicYearReq.setEndDate(java.time.LocalDate.now().plusMonths(6));
            academicYearReq.setWeeksCount(20); // 默认20周
            academicYearReq.setIsCurrent(false); // 默认不是当前学期
            academicYearReq.setStatus(1); // 默认启用状态

            academicYearService.createAcademicYear(academicYearReq);
            log.debug("创建学年学期信息: {}-{}", schoolYear, term);
        }

        processedAcademicYears.add(key);
    }

    /**
     * 生成学期代码
     */
    private String generateTermCode(String termName) {
        if (StrUtil.isBlank(termName)) {
            return "TERM_1";
        }

        if (termName.contains("第一学期") || termName.contains("上学期") || termName.contains("秋季")) {
            return "TERM_1";
        } else if (termName.contains("第二学期") || termName.contains("下学期") || termName.contains("春季")) {
            return "TERM_2";
        } else if (termName.contains("第三学期") || termName.contains("夏季")) {
            return "TERM_3";
        } else {
            return "TERM_1"; // 默认值
        }
    }

    /**
     * 生成教师工号
     */
    private String generateTeacherNo(String teacherName) {
        // 生成格式：T + 年份后两位 + 月份 + 日期 + 教师姓名首字母拼音 + 随机数
        LocalDateTime now = LocalDateTime.now();
        String yearMonth = String.format("%02d%02d%02d",
            now.getYear() % 100, now.getMonthValue(), now.getDayOfMonth());

        // 简单的姓名处理，实际项目中可能需要更复杂的拼音转换
        String nameCode = "00";
        if (teacherName.length() > 0) {
            int hashCode = Math.abs(teacherName.charAt(0));
            nameCode = String.format("%02d", hashCode % 100);
        }

        // 添加随机数确保唯一性
        int random = (int)(Math.random() * 100);

        return String.format("T%s%s%02d", yearMonth, nameCode, random);
    }

    /**
     * 从课程信息中提取主要教室（当基本信息中教室为空时使用）
     */
    private String extractMainClassroomFromCourses(VocationalClassScheduleImportExcelVO data) {
        // 收集所有课程中出现的教室
        Map<String, Integer> classroomCount = new HashMap<>();

        extractClassroomFromCourse(data.getMondayCourse(), classroomCount);
        extractClassroomFromCourse(data.getTuesdayCourse(), classroomCount);
        extractClassroomFromCourse(data.getWednesdayCourse(), classroomCount);
        extractClassroomFromCourse(data.getThursdayCourse(), classroomCount);
        extractClassroomFromCourse(data.getFridayCourse(), classroomCount);

        // 返回出现次数最多的教室
        return classroomCount.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse("");
    }

    /**
     * 从单个课程信息中提取教室并计数
     */
    private void extractClassroomFromCourse(String courseInfo, Map<String, Integer> classroomCount) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 匹配教室格式：B215、A301等
        Pattern classroomPattern = Pattern.compile("([A-Z]\\d{3,4})");
        Matcher matcher = classroomPattern.matcher(courseInfo);

        while (matcher.find()) {
            String classroom = matcher.group(1);
            classroomCount.put(classroom, classroomCount.getOrDefault(classroom, 0) + 1);
        }
    }

    /**
     * 从所有来源备份教师信息（基于准确的课程表格式：课程名称 教师名称 上课地点）
     */
    private void backupTeacherDataFromAllSources(VocationalClassScheduleImportExcelVO data, Set<String> processedTeachers) {
        // 1. 从班主任信息中提取教师信息，这是最可靠的来源
        if (StrUtil.isNotBlank(data.getClassTeacher())) {
            String teacherName = cleanTeacherName(data.getClassTeacher().trim());
            if (StrUtil.isNotBlank(teacherName) && isValidTeacherNameByPosition(teacherName)) {
                // 班主任不指定具体科目，留空让系统后续从课程中推断
                backupSingleTeacher(teacherName, "", processedTeachers);
                log.debug("从班主任信息提取教师: {}", teacherName);
            } else {
                log.debug("班主任姓名验证失败: {} (原始: {})", teacherName, data.getClassTeacher());
            }
        }

        // 2. 从课程信息中精确提取教师信息（基于固定格式：课程名称 教师名称 上课地点）
        extractTeacherFromStructuredCourse(data.getMondayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getTuesdayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getWednesdayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getThursdayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getFridayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getSaturdayCourse(), processedTeachers);
        extractTeacherFromStructuredCourse(data.getSundayCourse(), processedTeachers);

        // 3. 使用宽松模式补充提取可能遗漏的教师（降低验证标准）
        extractTeacherWithRelaxedValidation(data.getMondayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getTuesdayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getWednesdayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getThursdayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getFridayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getSaturdayCourse(), processedTeachers);
        extractTeacherWithRelaxedValidation(data.getSundayCourse(), processedTeachers);
    }

    /**
     * 清理教师姓名，移除职务后缀
     */
    private String cleanTeacherName(String rawName) {
        if (StrUtil.isBlank(rawName)) {
            return "";
        }

        // 移除常见的职务后缀
        String cleanName = rawName.replaceAll("(老师|教授|主任|讲师|副教授|助教)$", "");

        // 验证清理后的姓名是否有效
        if (isValidTeacherName(cleanName)) {
            return cleanName;
        }

        return "";
    }

    /**
     * 高置信度教师姓名验证（更严格的验证）
     */
    private boolean isHighConfidenceTeacherName(String name) {
        if (!isValidTeacherName(name)) {
            return false;
        }

        // 扩展的常见姓氏列表（包含更多姓氏）
        String[] commonSurnames = {
            // 百家姓前100位
            "王", "李", "张", "刘", "陈", "杨", "赵", "黄", "周", "吴",
            "徐", "孙", "胡", "朱", "高", "林", "何", "郭", "马", "罗",
            "梁", "宋", "郑", "谢", "韩", "唐", "冯", "于", "董", "萧",
            "程", "曹", "袁", "邓", "许", "傅", "沈", "曾", "彭", "吕",
            "苏", "卢", "蒋", "蔡", "贾", "丁", "魏", "薛", "叶", "阎",
            "余", "潘", "杜", "戴", "夏", "钟", "汪", "田", "任", "姜",
            // 补充更多常见姓氏
            "方", "石", "姚", "谭", "廖", "邹", "熊", "金", "陆", "郝",
            "孔", "白", "崔", "康", "毛", "邱", "秦", "江", "史", "顾",
            "侯", "邵", "孟", "龙", "万", "段", "雷", "钱", "汤", "尹",
            "黎", "易", "常", "武", "乔", "贺", "赖", "龚", "文", "庞",
            "樊", "兰", "殷", "施", "陶", "洪", "翟", "安", "颜", "倪",
            "严", "牛", "温", "芦", "季", "俞", "章", "鲁", "葛", "伍",
            "韦", "申", "尤", "毕", "聂", "丛", "焦", "向", "柳", "邢",
            "路", "岳", "齐", "沿", "梅", "莫", "庄", "辛", "管", "祝",
            "左", "涂", "谷", "祁", "时", "舒", "耿", "牟", "卜", "路",
            "詹", "关", "苗", "凌", "费", "纪", "靳", "盛", "童", "欧"
        };

        for (String surname : commonSurnames) {
            if (name.startsWith(surname)) {
                return true;
            }
        }

        // 如果不以常见姓氏开头，则认为不是高置信度的教师姓名
        log.debug("姓名 '{}' 不以常见姓氏开头，跳过提取", name);
        return false;
    }

    /**
     * 基于位置的教师姓名验证（不依赖姓氏库，适用于所有姓氏）
     * 基于课程表格式：课程名称 教师名称 上课地点
     */
    private boolean isValidTeacherNameByPosition(String name) {
        if (StrUtil.isBlank(name)) {
            return false;
        }

        // 基本长度验证：中文姓名通常2-4个字符
        if (name.length() < 1 || name.length() > 5) {
            return false;
        }

        // 排除明显的科目名称
        if (isValidSubject(name)) {
            log.debug("排除科目名称: {}", name);
            return false;
        }

        // 排除明显的地点信息
        if (isLocationKeyword(name)) {
            log.debug("排除地点信息: {}", name);
            return false;
        }

        // 排除数字和特殊字符
        if (name.matches(".*\\d.*")) {
            log.debug("排除包含数字的文本: {}", name);
            return false;
        }

        // 排除英文字母
        if (name.matches(".*[a-zA-Z].*")) {
            log.debug("排除包含英文的文本: {}", name);
            return false;
        }

        // 排除特殊符号
        if (name.matches(".*[\\u0021-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E].*")) {
            log.debug("排除包含特殊符号的文本: {}", name);
            return false;
        }

        // 排除明显的时间词汇
        String[] timeWords = {"上午", "下午", "晚上", "中午", "早上", "傍晚", "周一", "周二", "周三", "周四", "周五"};
        for (String timeWord : timeWords) {
            if (name.contains(timeWord)) {
                log.debug("排除时间词汇: {}", name);
                return false;
            }
        }

        // 排除其他明显不是姓名的词汇
        String[] excludeWords = {
            "课程", "班级", "学期", "学年", "年级", "实训", "实习", "考试", "作业",
            "第一", "第二", "第三", "第四", "第五", "第六", "第七", "第八",
            "早读", "晚修", "自习", "休息", "活动", "比赛", "训练", "讲座"
        };

        for (String exclude : excludeWords) {
            if (name.equals(exclude) || name.contains(exclude)) {
                log.debug("排除非姓名词汇: {}", name);
                return false;
            }
        }

        // 通过所有验证，认为是有效的教师姓名
        log.debug("基于位置验证通过的教师姓名: {}", name);
        return true;
    }

    /**
     * 检查是否为地点关键词
     */
    private boolean isLocationKeyword(String text) {
        String[] locationKeywords = {
            "教室", "机房", "实训室", "操场", "体育馆", "图书馆", "实验室",
            "茶艺室", "电工室", "汽修室", "烹饪室", "美术室", "音乐室",
            "会议室", "办公室", "阶梯", "大厅", "走廊", "楼层"
        };

        for (String keyword : locationKeywords) {
            if (text.contains(keyword)) {
                return true;
            }
        }

        // 检查是否为教室编号格式（如A301, B215等）
        if (text.matches("^[A-Z]\\d+$") || text.matches("^\\d+$")) {
            return true;
        }

        return false;
    }

    /**
     * 从结构化课程信息中提取教师信息
     * 支持多种格式：
     * 1. 一段式：课程名称（无教师信息）
     * 2. 两段式：课程名称 教师名称
     * 3. 三段式：课程名称 教师名称 地点
     * 4. 四段式：课程第一部分 课程第二部分 教师名称 地点
     */
    private void extractTeacherFromStructuredCourse(String courseInfo, Set<String> processedTeachers) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割课程信息
        String[] parts = courseInfo.trim().split("\\s+");

        log.debug("解析课程信息: {} -> 分割后: {}", courseInfo, Arrays.toString(parts));

        if (parts.length == 1) {
            // 一段式：课程名称（无教师信息）
            String courseName = parts[0].trim();

            log.debug("识别为一段式: 课程={} (无教师信息)", courseName);
            // 一段式不提取教师信息

        } else if (parts.length == 2) {
            // 两段式：课程名称 教师名称
            String courseName = parts[0].trim();
            String teacherName = parts[1].trim();

            log.debug("识别为两段式: 课程={}, 教师={}", courseName, teacherName);

            // 提取教师信息
            extractAndBackupTeacher(teacherName, courseName, processedTeachers);

        } else if (parts.length == 3) {
            // 三段式：课程名称 教师名称 地点
            String courseName = parts[0].trim();
            String teacherName = parts[1].trim();
            String location = parts[2].trim();

            log.debug("识别为三段式: 课程={}, 教师={}, 地点={}", courseName, teacherName, location);

            // 提取教师信息
            extractAndBackupTeacher(teacherName, courseName, processedTeachers);

        } else if (parts.length == 4) {
            // 四段式：课程第一部分 课程第二部分 教师名称 地点
            String courseNamePart1 = parts[0].trim();
            String courseNamePart2 = parts[1].trim();
            String teacherName = parts[2].trim();
            String location = parts[3].trim();

            String fullCourseName = courseNamePart1 + courseNamePart2; // 合并课程名称

            log.debug("识别为四段式: 课程={}({}+{}), 教师={}, 地点={}",
                fullCourseName, courseNamePart1, courseNamePart2, teacherName, location);

            // 提取教师信息
            extractAndBackupTeacher(teacherName, fullCourseName, processedTeachers);

        } else if (parts.length > 4) {
            // 超过四段：可能地点名称包含空格，按四段式处理
            String courseNamePart1 = parts[0].trim();
            String courseNamePart2 = parts[1].trim();
            String teacherName = parts[2].trim();

            // 后面的都作为地点
            StringBuilder locationBuilder = new StringBuilder();
            for (int i = 3; i < parts.length; i++) {
                if (i > 3) locationBuilder.append(" ");
                locationBuilder.append(parts[i].trim());
            }
            String location = locationBuilder.toString();

            String fullCourseName = courseNamePart1 + courseNamePart2;

            log.debug("识别为多段式: 课程={}({}+{}), 教师={}, 地点={}",
                fullCourseName, courseNamePart1, courseNamePart2, teacherName, location);

            // 提取教师信息
            extractAndBackupTeacher(teacherName, fullCourseName, processedTeachers);

        } else {
            log.debug("课程信息格式不符合预期: {} (分割后部分数: {})", courseInfo, parts.length);
        }
    }

    /**
     * 提取并备份教师信息的通用方法
     */
    private void extractAndBackupTeacher(String teacherName, String courseName, Set<String> processedTeachers) {
        // 清理教师姓名（移除可能的职务后缀）
        String cleanedTeacherName = cleanTeacherName(teacherName);

        log.debug("教师姓名清理: {} -> {}", teacherName, cleanedTeacherName);

        // 基于位置的教师姓名验证（不依赖关键字）
        if (StrUtil.isNotBlank(cleanedTeacherName) && isValidTeacherNameByPosition(cleanedTeacherName)) {
            backupSingleTeacher(cleanedTeacherName, courseName, processedTeachers);
            log.debug("✅ 成功提取教师: {} (课程: {})", cleanedTeacherName, courseName);
        } else {
            log.debug("❌ 教师姓名验证失败: {} (原始: {}, 课程: {})", cleanedTeacherName, teacherName, courseName);
        }
    }

    /**
     * 使用宽松验证提取教师信息（用于补充提取可能遗漏的教师）
     * 主要针对包含"老师"等关键词的情况
     */
    private void extractTeacherWithRelaxedValidation(String courseInfo, Set<String> processedTeachers) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割课程信息
        String[] parts = courseInfo.trim().split("\\s+");

        // 标准格式应该是3部分：课程名称 教师名称 上课地点
        if (parts.length >= 2) {
            String teacherCandidate = parts[1].trim();  // 第二部分可能是教师姓名

            // 如果已经处理过这个教师，跳过
            String cleanedName = cleanTeacherName(teacherCandidate);
            if (processedTeachers.contains(cleanedName)) {
                return;
            }

            // 宽松验证：检查是否包含教师相关关键词
            if (teacherCandidate.contains("老师") ||
                teacherCandidate.contains("教师") ||
                teacherCandidate.contains("讲师") ||
                teacherCandidate.contains("教授")) {

                // 提取科目（如果第一部分是有效科目）
                String subject = "";
                if (parts.length >= 1 && isValidSubject(parts[0].trim())) {
                    subject = parts[0].trim();
                }

                // 备份教师信息
                backupSingleTeacher(cleanedName, subject, processedTeachers);
                log.debug("宽松模式提取教师: {} (原始: {}, 科目: {})", cleanedName, teacherCandidate, subject);
            }
        }
    }

    /**
     * 从课程信息中提取并备份教师信息（旧方法，保留作为备用）
     */
    private void extractAndBackupTeachersFromCourse(String courseInfo, Set<String> processedTeachers) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 采用更保守和精确的教师姓名提取策略

        // 1. 优先匹配明确的 "教师姓名+老师/教授等" 格式
        Pattern explicitTeacherPattern = Pattern.compile("([\\u4e00-\\u9fa5]{2,4})(老师|教授|主任|讲师)");
        Matcher explicitMatcher = explicitTeacherPattern.matcher(courseInfo);

        while (explicitMatcher.find()) {
            String rawTeacherName = explicitMatcher.group(1);

            if (isValidTeacherName(rawTeacherName)) {
                // 尝试从上下文提取科目
                String subject = extractSubjectFromContext(courseInfo, rawTeacherName + explicitMatcher.group(2));
                backupSingleTeacher(rawTeacherName, subject, processedTeachers);
            }
        }

        // 2. 匹配 "科目+空格+教师姓名+老师" 的明确格式
        Pattern subjectTeacherPattern = Pattern.compile("([\\u4e00-\\u9fa5]+)\\s+([\\u4e00-\\u9fa5]{2,4})(老师|教授|主任|讲师)");
        Matcher subjectMatcher = subjectTeacherPattern.matcher(courseInfo);

        while (subjectMatcher.find()) {
            String subject = subjectMatcher.group(1);
            String teacherName = subjectMatcher.group(2);

            if (isValidTeacherName(teacherName) && isValidSubject(subject)) {
                backupSingleTeacher(teacherName, subject, processedTeachers);
            }
        }

        // 注意：不再提取没有明确"老师"等标识的姓名，避免误提取科目名称
    }

    /**
     * 备份单个教师信息（智能处理科目信息，避免将职务作为科目）
     */
    private void backupSingleTeacher(String teacherName, String subject, Set<String> processedTeachers) {
        if (StrUtil.isBlank(teacherName)) {
            return;
        }

        // 检查教师是否已存在
        cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO teacherPageReq =
                new cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO();
        teacherPageReq.setTeacherName(teacherName);
        teacherPageReq.setPageSize(1);

        List<TeacherDO> existingTeachers = teacherService.getTeacherPage(teacherPageReq).getList();

        if (existingTeachers.isEmpty()) {
            // 创建新教师信息
            TeacherSaveReqVO teacherReq = new TeacherSaveReqVO();
            teacherReq.setTeacherName(teacherName);
            // 生成默认工号（基于教师姓名和时间戳）
            teacherReq.setTeacherNo(generateTeacherNo(teacherName));
            // 如果科目是有效科目，可以设置为专业或备注信息
            if (StrUtil.isNotBlank(subject) && isValidSubject(subject)) {
                teacherReq.setMajor(subject); // 将科目信息存储为专业字段
            }

            teacherService.createTeacher(teacherReq);
            log.debug("创建教师信息: {} (专业: {})", teacherName, subject);
        } else if (StrUtil.isNotBlank(subject) && isValidSubject(subject)) {
            // 教师已存在，但如果当前科目更具体且有效，则更新专业信息
            TeacherDO existingTeacher = existingTeachers.get(0);
            String currentMajor = existingTeacher.getMajor();

            // 如果现有专业为空，则更新为新专业
            if (StrUtil.isBlank(currentMajor)) {
                TeacherSaveReqVO updateReq = new TeacherSaveReqVO();
                updateReq.setTeacherId(existingTeacher.getTeacherId());
                updateReq.setTeacherName(teacherName);
                updateReq.setTeacherNo(existingTeacher.getTeacherNo());
                updateReq.setMajor(subject);

                teacherService.updateTeacher(updateReq);
                log.debug("更新教师专业信息: {} (专业: 空 -> {})", teacherName, subject);
            }
        }

        processedTeachers.add(teacherName);
    }

    /**
     * 验证是否为有效的教师姓名
     */
    private boolean isValidTeacherName(String name) {
        if (StrUtil.isBlank(name) || name.length() < 2 || name.length() > 4) {
            return false;
        }

        // 排除科目名称和其他明显不是教师姓名的词汇
        String[] excludeWords = {
            // 科目名称
            "语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理", "政治", "思政",
            "计算机", "编程", "软件", "网络", "数据库", "美术", "音乐", "体育", "舞蹈",
            "茶艺", "电工", "机械", "汽修", "烹饪", "护理", "会计", "营销", "电商",
            "德育", "心理", "职业", "就业", "创业", "实训", "实习", "专业", "技能",
            // 其他词汇
            "课程", "班级", "教室", "机房", "实训", "上课", "下课", "休息", "自习",
            "考试", "作业", "练习", "复习", "预习", "讲座", "活动", "比赛", "训练",
            "早读", "晚修", "课间", "午休", "放学", "开学", "期中", "期末", "月考",
            "第一", "第二", "第三", "第四", "第五", "第六", "第七", "第八",
            "周一", "周二", "周三", "周四", "周五", "周六", "周日",
            "上午", "下午", "晚上", "中午", "早上", "早晨", "傍晚",
            // 常见的非姓名词汇
            "班主", "任课", "代课", "助教", "辅导", "指导", "负责", "管理",
            "春季", "秋季", "学期", "学年", "年级", "届", "级", "班",
            // 数字相关
            "一", "二", "三", "四", "五", "六", "七", "八", "九", "十",
            // 其他可能误识别的词
            "技能", "专业", "基础", "应用", "实践", "理论", "综合", "选修", "必修"
        };

        for (String exclude : excludeWords) {
            if (name.equals(exclude)) {
                return false;
            }
        }

        // 检查是否包含数字（教师姓名通常不包含数字）
        if (name.matches(".*\\d.*")) {
            return false;
        }

        // 检查是否包含英文字母（教师姓名通常不包含英文）
        if (name.matches(".*[a-zA-Z].*")) {
            return false;
        }

        // 检查是否包含特殊符号
        // 检查是否包含标点或特殊符号（使用 Java 支持的 Unicode 范围）
        if (name.matches(".*[\\u0021-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E].*")) {

            return false;
        }

        // 基本验证通过，但不做姓氏验证（在高置信度验证中进行）
        return true;
    }

    /**
     * 验证是否为有效的科目名称
     */
    private boolean isValidSubject(String subject) {
        if (StrUtil.isBlank(subject)) {
            return false;
        }

        // 有效的科目列表（基于实际课程表中的科目）
        String[] validSubjects = {
            // 基础学科
            "语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理", "政治", "思政",
            // 计算机相关
            "计算机", "编程", "软件", "网络", "数据库", "信息技术", "计算机应用", "程序设计",
            "网页设计", "平面设计", "UI设计", "软件开发", "系统维护", "办公软件",
            // 艺术体育
            "美术", "音乐", "体育", "舞蹈", "绘画", "声乐", "器乐", "钢琴", "吉他", "小提琴",
            // 职业技能
            "茶艺", "电工", "机械", "汽修", "烹饪", "护理", "会计", "营销", "电商",
            "机电", "电子", "建筑", "装饰", "服装", "美容", "美发", "摄影", "化妆",
            // 传统文化经典
            "论语", "国学", "古诗词", "古文", "诗经", "唐诗", "宋词", "古典文学",
            "传统文化", "中华文化", "文言文", "古代文学", "经典诵读", "三字经",
            "弟子规", "千字文", "百家姓", "孟子", "大学", "中庸", "老子", "庄子",
            // 德育心理
            "德育", "心理", "心理健康", "职业规划", "就业指导", "创业", "创新创业",
            "思想品德", "道德修养", "人生规划", "职业素养",
            // 学习能力培养
            "学习力", "学习方法", "学习技能", "学习能力", "自主学习", "学习策略",
            // 实训实习
            "实训", "实习", "专业实训", "综合实训", "顶岗实习", "校外实习", "企业实训",
            // 语言文字
            "礼仪", "普通话", "书法", "阅读", "写作", "口语", "听力", "演讲", "朗诵",
            "硬笔书法", "毛笔书法", "汉字书写", "规范字", "应用文写作",
            // 社会科学
            "安全教育", "法律", "经济", "管理", "市场", "财务", "社会学", "哲学",
            "法制教育", "消防安全", "交通安全", "食品安全",
            // 专业课程（可能的变体）
            "专业课", "基础课", "选修课", "必修课", "核心课程", "拓展课", "兴趣课",
            // 生活技能
            "生活技能", "家政", "手工", "园艺", "养生", "健康教育", "营养学",
            // 现代技能
            "电商运营", "新媒体", "短视频", "直播", "社交媒体", "数字营销"
        };

        // 精确匹配或包含匹配
        for (String validSubject : validSubjects) {
            if (subject.equals(validSubject) || subject.contains(validSubject)) {
                return true;
            }
        }

        // 检查是否为常见的课程格式（如：计算机基础、英语口语等）
        if (subject.length() >= 2 && subject.length() <= 15) {
            // 如果包含基础学科关键词，也认为是有效的
            String[] coreSubjects = {
                "语文", "数学", "英语", "计算机", "体育", "美术", "音乐", "学习",
                "论语", "国学", "古诗", "文化", "书法", "德育", "心理", "安全"
            };
            for (String core : coreSubjects) {
                if (subject.contains(core)) {
                    return true;
                }
            }

            // 检查是否为专业课程格式（包含"课程"、"专业"等关键词）
            String[] courseKeywords = {
                "课程", "专业", "技能", "训练", "培训", "教育", "指导", "实训", "实习",
                "基础", "应用", "实践", "理论", "综合", "核心", "选修", "必修"
            };
            for (String keyword : courseKeywords) {
                if (subject.contains(keyword)) {
                    return true;
                }
            }

            // 检查是否为设计类课程
            if (subject.contains("设计") && subject.length() >= 3) {
                return true;
            }

            // 检查是否为传统文化相关
            String[] culturalKeywords = {"古", "传统", "经典", "文学", "诗", "词", "文言"};
            for (String keyword : culturalKeywords) {
                if (subject.contains(keyword)) {
                    return true;
                }
            }

            // 检查是否为技能类课程
            String[] skillKeywords = {"技术", "操作", "维护", "管理", "运营", "营销"};
            for (String keyword : skillKeywords) {
                if (subject.contains(keyword)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 从上下文中提取科目信息
     */
    private String extractSubjectFromContext(String courseInfo, String teacherName) {
        if (StrUtil.isBlank(courseInfo)) {
            return "";
        }

        // 在教师姓名前面查找科目
        int teacherIndex = courseInfo.indexOf(teacherName);
        if (teacherIndex > 0) {
            String beforeTeacher = courseInfo.substring(0, teacherIndex).trim();

            // 常见科目关键词
            String[] subjects = {
                "语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理", "政治", "思政",
                "计算机", "编程", "软件", "网络", "数据库", "美术", "音乐", "体育", "舞蹈",
                "茶艺", "电工", "机械", "汽修", "烹饪", "护理", "会计", "营销", "电商"
            };

            // 从后往前查找最近的科目
            for (String subject : subjects) {
                if (beforeTeacher.endsWith(subject)) {
                    return subject;
                }
            }

            // 如果没有找到结尾匹配的，尝试查找包含的科目
            for (String subject : subjects) {
                if (beforeTeacher.contains(subject)) {
                    return subject;
                }
            }
        }

        return "";
    }

    /**
     * 从所有来源备份场地信息（基于准确的课程表格式）
     */
    private void backupLocationDataFromAllSources(VocationalClassScheduleImportExcelVO data, Set<String> processedLocations) {
        // 1. 从教室信息中提取
        if (StrUtil.isNotBlank(data.getClassroom())) {
            String locationType = determineLocationType(data.getClassroom());
            backupSingleLocation(data.getClassroom().trim(), locationType, processedLocations);
        }

        // 2. 从各天课程信息中精确提取场地信息（基于固定格式：课程名称 教师名称 上课地点）
        extractLocationFromStructuredCourse(data.getMondayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getTuesdayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getWednesdayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getThursdayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getFridayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getSaturdayCourse(), processedLocations);
        extractLocationFromStructuredCourse(data.getSundayCourse(), processedLocations);
    }

    /**
     * 从结构化课程信息中提取场地信息
     * 支持多种格式：
     * 1. 一段式：课程名称（无地点信息）
     * 2. 两段式：课程名称 教师名称（无地点信息）
     * 3. 三段式：课程名称 教师名称 地点
     * 4. 四段式：课程第一部分 课程第二部分 教师名称 地点
     */
    private void extractLocationFromStructuredCourse(String courseInfo, Set<String> processedLocations) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割课程信息
        String[] parts = courseInfo.trim().split("\\s+");

        log.debug("解析课程信息提取地点: {} -> 分割后: {}", courseInfo, Arrays.toString(parts));

        if (parts.length == 1) {
            // 一段式：课程名称（无地点信息）
            log.debug("一段式格式，无地点信息: {}", courseInfo);
            // 不提取地点

        } else if (parts.length == 2) {
            // 两段式：课程名称 教师名称（无地点信息）
            log.debug("两段式格式，无地点信息: {}", courseInfo);
            // 不提取地点

        } else if (parts.length == 3) {
            // 三段式：课程名称 教师名称 地点
            String courseName = parts[0].trim();
            String teacherName = parts[1].trim();
            String location = parts[2].trim();

            log.debug("识别为三段式: 课程={}, 教师={}, 地点={}", courseName, teacherName, location);

            // 提取地点信息
            extractAndBackupLocation(location, courseName, processedLocations);

        } else if (parts.length == 4) {
            // 四段式：课程第一部分 课程第二部分 教师名称 地点
            String courseNamePart1 = parts[0].trim();
            String courseNamePart2 = parts[1].trim();
            String teacherName = parts[2].trim();
            String location = parts[3].trim();

            String fullCourseName = courseNamePart1 + courseNamePart2;

            log.debug("识别为四段式: 课程={}({}+{}), 教师={}, 地点={}",
                fullCourseName, courseNamePart1, courseNamePart2, teacherName, location);

            // 提取地点信息
            extractAndBackupLocation(location, fullCourseName, processedLocations);

        } else if (parts.length > 4) {
            // 超过四段：可能地点名称包含空格
            String courseNamePart1 = parts[0].trim();
            String courseNamePart2 = parts[1].trim();
            String teacherName = parts[2].trim();

            // 后面的都作为地点
            StringBuilder locationBuilder = new StringBuilder();
            for (int i = 3; i < parts.length; i++) {
                if (i > 3) locationBuilder.append(" ");
                locationBuilder.append(parts[i].trim());
            }
            String location = locationBuilder.toString();

            String fullCourseName = courseNamePart1 + courseNamePart2;

            log.debug("识别为多段式: 课程={}({}+{}), 教师={}, 地点={}",
                fullCourseName, courseNamePart1, courseNamePart2, teacherName, location);

            // 提取地点信息
            extractAndBackupLocation(location, fullCourseName, processedLocations);

        } else {
            log.debug("课程信息格式不完整，无法提取场地: {} (分割后部分数: {})", courseInfo, parts.length);
        }
    }

    /**
     * 提取并备份地点信息的通用方法
     */
    private void extractAndBackupLocation(String location, String courseName, Set<String> processedLocations) {
        if (StrUtil.isBlank(location)) {
            return;
        }

        log.debug("地点验证: {}", location);

        // 基于位置的地点验证（不依赖关键字）
        if (isValidLocationByPosition(location)) {
            String locationType = determineLocationType(location);
            backupSingleLocation(location, locationType, processedLocations);
            log.debug("✅ 成功提取地点: {} (类型: {}, 课程: {})", location, locationType, courseName);
        } else {
            log.debug("❌ 地点验证失败: {} (课程: {})", location, courseName);
        }
    }

    /**
     * 从课程信息中提取并备份场地信息
     */
    private void extractAndBackupLocationsFromCourse(String courseInfo, Set<String> processedLocations) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 提取场地信息的正则表达式模式
        // 匹配常见格式：B215、机房1、实训室A、舞蹈室、茶艺室等
        Pattern locationPattern = Pattern.compile("([A-Z]\\d{3,4}|[\\u4e00-\\u9fa5]*[机实舞茶画电工][房室]\\d*[A-Z]?)");
        Matcher matcher = locationPattern.matcher(courseInfo);

        while (matcher.find()) {
            String locationName = matcher.group(1);
            String locationType = determineLocationType(locationName);
            backupSingleLocation(locationName, locationType, processedLocations);
        }
    }

    /**
     * 备份单个场地信息
     */
    private void backupSingleLocation(String locationName, String locationType, Set<String> processedLocations) {
        if (StrUtil.isBlank(locationName) || processedLocations.contains(locationName)) {
            return;
        }

        // 检查场地是否已存在
        cn.iocoder.yudao.module.ao.controller.admin.classlocation.vo.ClassLocationPageReqVO locationPageReq =
                new cn.iocoder.yudao.module.ao.controller.admin.classlocation.vo.ClassLocationPageReqVO();
        locationPageReq.setLocationName(locationName);
        locationPageReq.setPageSize(1);

        if (classLocationService.getClassLocationPage(locationPageReq).getList().isEmpty()) {
            // 创建场地信息
            ClassLocationSaveReqVO locationReq = new ClassLocationSaveReqVO();
            locationReq.setLocationName(locationName);
            locationReq.setLocationType(locationType);

            classLocationService.createClassLocation(locationReq);
            log.debug("创建场地信息: {} (类型: {})", locationName, locationType);
        }

        processedLocations.add(locationName);
    }

    /**
     * 智能判断场地类型
     */
    private String determineLocationType(String locationName) {
        if (StrUtil.isBlank(locationName)) {
            return "教室";
        }

        String name = locationName.toLowerCase();

        // 机房类型
        if (name.contains("机房") || name.contains("计算机") || name.contains("电脑")) {
            return "机房";
        }

        // 实训室类型
        if (name.contains("实训室") || name.contains("实训")) {
            return "实训室";
        }

        // 舞蹈室类型
        if (name.contains("舞蹈室") || name.contains("舞蹈")) {
            return "舞蹈室";
        }

        // 茶艺室类型
        if (name.contains("茶艺室") || name.contains("茶艺")) {
            return "茶艺室";
        }

        // 画室类型
        if (name.contains("画室") || name.contains("美术室") || name.contains("绘画")) {
            return "画室";
        }

        // 电工室类型
        if (name.contains("电工室") || name.contains("电工") || name.contains("电气")) {
            return "电工室";
        }

        // 实验室类型
        if (name.contains("实验室") || name.contains("实验")) {
            return "实验室";
        }

        // 体育场地
        if (name.contains("体育馆") || name.contains("操场") || name.contains("球场")) {
            return "体育场地";
        }

        // 默认为教室
        return "教室";
    }

    /**
     * 从学期信息中提取学年
     */
    private String extractSchoolYear(String termInfo) {
        if (StrUtil.isBlank(termInfo)) return "";

        // 匹配如 "2024-2025学年 第二学期" 中的 "2024-2025"
        Pattern pattern = Pattern.compile("(\\d{4}-\\d{4})学年");
        Matcher matcher = pattern.matcher(termInfo);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return "";
    }

    /**
     * 从学期信息中提取学期
     */
    private String extractTerm(String termInfo) {
        if (StrUtil.isBlank(termInfo)) return "";

        // 匹配如 "2024-2025学年 第二学期" 中的 "第二学期"
        Pattern pattern = Pattern.compile("(第[一二三四]学期)");
        Matcher matcher = pattern.matcher(termInfo);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return "";
    }

    /**
     * 从所有来源备份课程信息
     */
    private void backupCourseDataFromAllSources(VocationalClassScheduleImportExcelVO data, Set<String> processedCourses) {
        log.debug("开始从班级{}的课程信息中提取课程", data.getClassName());

        // 从各天课程信息中提取课程名称
        extractCourseFromStructuredCourse(data.getMondayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getTuesdayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getWednesdayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getThursdayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getFridayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getSaturdayCourse(), processedCourses);
        extractCourseFromStructuredCourse(data.getSundayCourse(), processedCourses);

        log.debug("班级{}课程提取完成，当前已处理课程数量: {}", data.getClassName(), processedCourses.size());
    }

    /**
     * 从结构化课程信息中提取课程名称
     * 支持多种格式：
     * 1. 一段式：课程名称
     * 2. 两段式：课程名称 教师名称
     * 3. 三段式：课程名称 教师名称 地点
     * 4. 四段式：课程第一部分 课程第二部分 教师名称 地点
     */
    private void extractCourseFromStructuredCourse(String courseInfo, Set<String> processedCourses) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割课程信息
        String[] parts = courseInfo.trim().split("\\s+");

        log.debug("解析课程信息提取课程: {} -> 分割后: {}", courseInfo, Arrays.toString(parts));

        if (parts.length == 2) {
            // 两段式：课程名称 教师名称
            String courseName = parts[0].trim();

            log.debug("识别为两段式: 课程={}", courseName);

            // 提取课程信息
            extractAndBackupCourse(courseName, processedCourses);

        } else if (parts.length == 3) {
            // 三段式：课程名称 教师名称 地点
            String courseName = parts[0].trim();

            log.debug("识别为三段式: 课程={}", courseName);

            // 提取课程信息
            extractAndBackupCourse(courseName, processedCourses);

        } else if (parts.length >= 4) {
            // 四段式或更多：课程第一部分 课程第二部分 教师名称 地点
            String courseNamePart1 = parts[0].trim();
            String courseNamePart2 = parts[1].trim();

            String fullCourseName = courseNamePart1 + courseNamePart2; // 合并课程名称

            log.debug("识别为四段式: 课程={}({}+{})", fullCourseName, courseNamePart1, courseNamePart2);

            // 提取课程信息
            extractAndBackupCourse(fullCourseName, processedCourses);

        } else {
            log.debug("课程信息格式不符合预期: {} (分割后部分数: {})", courseInfo, parts.length);
        }
    }

    /**
     * 提取并备份课程信息的通用方法
     */
    private void extractAndBackupCourse(String courseName, Set<String> processedCourses) {
        if (StrUtil.isBlank(courseName)) {
            return;
        }

        log.debug("课程验证: {}", courseName);

        // 基于位置的课程验证（不依赖关键字）
        if (isValidCourseByPosition(courseName)) {
            backupSingleCourse(courseName, processedCourses);
            log.debug("✅ 成功提取课程: {}", courseName);
        } else {
            log.debug("❌ 课程验证失败: {}", courseName);
        }
    }

    /**
     * 备份单个课程信息
     */
    private void backupSingleCourse(String courseName, Set<String> processedCourses) {
        if (StrUtil.isBlank(courseName) || processedCourses.contains(courseName)) {
            return;
        }

        try {
            // 检查课程是否已存在
            cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO coursePageReq =
                    new cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO();
            coursePageReq.setCourseName(courseName);
            coursePageReq.setPageSize(1);

            if (courseService.getCoursePage(coursePageReq).getList().isEmpty()) {
                // 创建课程信息
                CourseSaveReqVO courseReq = new CourseSaveReqVO();
                courseReq.setCourseName(courseName);

                // 生成课程编码：使用课程名称的拼音首字母 + 时间戳后4位
                String courseCode = generateCourseCode(courseName);
                courseReq.setCourseCode(courseCode);

                // 设置默认值
                courseReq.setCourseType("必修课");  // 默认课程类型
                courseReq.setCourseCategory("专业课");  // 默认课程类别
                courseReq.setStatus(1);  // 默认启用状态

                // 设置学时相关字段的默认值
                courseReq.setCreditHours(32);  // 默认课程学时
                courseReq.setTheoryHours(24);  // 默认理论学时
                courseReq.setPracticeHours(8);  // 默认实践学时
                courseReq.setCredits(new java.math.BigDecimal("2.0"));  // 默认学分

                courseService.createCourse(courseReq);
                log.debug("创建课程信息: {} (编码: {})", courseName, courseCode);
            }

            processedCourses.add(courseName);
        } catch (Exception e) {
            log.warn("备份课程信息失败: {}, 错误: {}", courseName, e.getMessage());
        }
    }

    /**
     * 生成课程编码
     * 规则：课程名称拼音首字母 + 时间戳后4位
     */
    private String generateCourseCode(String courseName) {
        if (StrUtil.isBlank(courseName)) {
            return "AUTO" + System.currentTimeMillis() % 10000;
        }

        StringBuilder codeBuilder = new StringBuilder();

        // 提取中文字符的拼音首字母
        for (char c : courseName.toCharArray()) {
            if (c >= 0x4e00 && c <= 0x9fa5) { // 中文字符范围
                // 简单的拼音首字母映射（可以使用更完善的拼音库）
                String pinyin = getPinyinFirstLetter(c);
                codeBuilder.append(pinyin);
            } else if (Character.isLetterOrDigit(c)) {
                // 保留字母和数字
                codeBuilder.append(Character.toUpperCase(c));
            }
        }

        // 如果没有提取到任何字符，使用默认前缀
        if (codeBuilder.length() == 0) {
            codeBuilder.append("COURSE");
        }

        // 限制长度并添加时间戳
        String prefix = codeBuilder.length() > 6 ? codeBuilder.substring(0, 6) : codeBuilder.toString();
        String timestamp = String.valueOf(System.currentTimeMillis() % 10000);

        return prefix + timestamp;
    }

    /**
     * 获取中文字符的拼音首字母（简化版本）
     */
    private String getPinyinFirstLetter(char c) {
        // 这是一个简化的映射，实际项目中建议使用专业的拼音库如pinyin4j
        if (c >= '啊' && c <= '驳') return "A";
        if (c >= '擦' && c <= '错') return "C";
        if (c >= '搭' && c <= '鵽') return "D";
        if (c >= '蛾' && c <= '贰') return "E";
        if (c >= '发' && c <= '馥') return "F";
        if (c >= '该' && c <= '过') return "G";
        if (c >= '哈' && c <= '夯') return "H";
        if (c >= '击' && c <= '驹') return "J";
        if (c >= '喀' && c <= '困') return "K";
        if (c >= '垃' && c <= '驴') return "L";
        if (c >= '妈' && c <= '穆') return "M";
        if (c >= '拿' && c <= '女') return "N";
        if (c >= '哦' && c <= '沤') return "O";
        if (c >= '帕' && c <= '曝') return "P";
        if (c >= '期' && c <= '群') return "Q";
        if (c >= '然' && c <= '若') return "R";
        if (c >= '撒' && c <= '所') return "S";
        if (c >= '塌' && c <= '拖') return "T";
        if (c >= '挖' && c <= '物') return "W";
        if (c >= '昔' && c <= '寻') return "X";
        if (c >= '压' && c <= '语') return "Y";
        if (c >= '匝' && c <= '做') return "Z";

        return "X"; // 默认返回X
    }

    /**
     * 测试课程创建功能（用于调试）
     */
    public void testCourseCreation() {
        log.info("=== 开始测试课程创建功能 ===");

        try {
            // 测试创建一个简单的课程
            String testCourseName = "测试课程" + System.currentTimeMillis();

            CourseSaveReqVO courseReq = new CourseSaveReqVO();
            courseReq.setCourseName(testCourseName);
            courseReq.setCourseCode("TEST" + System.currentTimeMillis() % 10000);
            courseReq.setCourseType("必修课");
            courseReq.setCourseCategory("专业课");
            courseReq.setStatus(1);

            // 设置学时相关字段
            courseReq.setCreditHours(32);
            courseReq.setTheoryHours(24);
            courseReq.setPracticeHours(8);
            courseReq.setCredits(new java.math.BigDecimal("2.0"));

            log.info("准备创建测试课程: {}", courseReq);

            Long courseId = courseService.createCourse(courseReq);
            log.info("✅ 测试课程创建成功，ID: {}", courseId);

            // 验证课程是否真的创建了
            cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO pageReq =
                new cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO();
            pageReq.setCourseName(testCourseName);
            pageReq.setPageSize(1);

            List<CourseDO> courses = courseService.getCoursePage(pageReq).getList();
            if (!courses.isEmpty()) {
                log.info("✅ 课程验证成功，找到课程: {}", courses.get(0));
            } else {
                log.error("❌ 课程验证失败，未找到创建的课程");
            }

        } catch (Exception e) {
            log.error("❌ 测试课程创建失败", e);
        }

        log.info("=== 课程创建功能测试完成 ===");
    }

    /**
     * 清理无效的教师数据（管理员手动调用）
     * 删除那些明显不是教师姓名的记录
     */
    public void cleanInvalidTeacherData() {
        log.info("开始清理无效教师数据...");

        try {
            // 获取所有教师数据
            cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO pageReq =
                    new cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO();
            pageReq.setPageSize(1000); // 获取大量数据

            List<cn.iocoder.yudao.module.ao.dal.dataobject.teacher.TeacherDO> allTeachers =
                    teacherService.getTeacherPage(pageReq).getList();

            List<Long> invalidTeacherIds = new ArrayList<>();

            for (cn.iocoder.yudao.module.ao.dal.dataobject.teacher.TeacherDO teacher : allTeachers) {
                String teacherName = teacher.getTeacherName();

                // 检查是否为无效的教师姓名（使用基于位置的验证）
                if (!isValidTeacherNameByPosition(teacherName)) {
                    invalidTeacherIds.add(teacher.getTeacherId());
                    log.info("标记删除无效教师: {} (ID: {})", teacherName, teacher.getTeacherId());
                }
            }

            // 批量删除无效教师数据
            if (!invalidTeacherIds.isEmpty()) {
                teacherService.deleteTeacherListByIds(invalidTeacherIds);
                log.info("成功删除 {} 条无效教师数据", invalidTeacherIds.size());
            } else {
                log.info("没有发现无效教师数据");
            }

        } catch (Exception e) {
            log.error("清理无效教师数据失败", e);
        }
    }

    /**
     * 清理教师信息中的无效科目（如"班主任"等职务）
     */
    public void cleanInvalidTeacherSubjects() {
        log.info("开始清理教师信息中的无效科目...");

        try {
            // 获取所有教师数据
            cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO pageReq =
                    new cn.iocoder.yudao.module.ao.controller.admin.teacher.vo.TeacherPageReqVO();
            pageReq.setPageSize(1000); // 获取大量数据

            List<TeacherDO> allTeachers = teacherService.getTeacherPage(pageReq).getList();

            int updatedCount = 0;

            for (TeacherDO teacher : allTeachers) {
                String currentMajor = teacher.getMajor();

                // 检查是否为无效的专业（职务等）
                if (StrUtil.isNotBlank(currentMajor) && !isValidSubject(currentMajor)) {
                    // 清空无效专业，将其移动到职务字段
                    TeacherSaveReqVO updateReq = new TeacherSaveReqVO();
                    updateReq.setTeacherId(teacher.getTeacherId());
                    updateReq.setTeacherName(teacher.getTeacherName());
                    updateReq.setTeacherNo(teacher.getTeacherNo());
                    updateReq.setMajor(""); // 清空无效专业
                    // 如果是职务相关的信息，可以设置到职务字段
                    if (StrUtil.isBlank(teacher.getPosition())) {
                        updateReq.setPosition(currentMajor);
                    }

                    teacherService.updateTeacher(updateReq);
                    log.info("清理教师无效专业: {} (专业: {} -> 空)", teacher.getTeacherName(), currentMajor);
                    updatedCount++;
                }
            }

            log.info("成功清理 {} 个教师的无效科目信息", updatedCount);

        } catch (Exception e) {
            log.error("清理教师无效科目失败", e);
        }
    }

    /**
     * 强制提取所有可能的教师姓名（用于补充遗漏的教师）
     * 使用最宽松的验证标准
     */
    public void forceExtractAllPossibleTeachers() {
        log.info("开始强制提取所有可能的教师姓名...");

        try {
            // 获取所有中高技班级课数据
            List<VocationalClassScheduleDO> allSchedules = vocationalClassScheduleMapper.selectList(new LambdaQueryWrapperX<>());

            Set<String> extractedTeachers = new HashSet<>();

            for (VocationalClassScheduleDO schedule : allSchedules) {
                // 从班主任信息中提取
                extractTeacherWithMinimalValidation(schedule.getClassTeacher(), extractedTeachers);

                // 从各天课程中提取
                extractTeacherWithMinimalValidation(schedule.getMondayCourse(), extractedTeachers);
                extractTeacherWithMinimalValidation(schedule.getTuesdayCourse(), extractedTeachers);
                extractTeacherWithMinimalValidation(schedule.getWednesdayCourse(), extractedTeachers);
                extractTeacherWithMinimalValidation(schedule.getThursdayCourse(), extractedTeachers);
                extractTeacherWithMinimalValidation(schedule.getFridayCourse(), extractedTeachers);
            }

            log.info("强制提取完成，共发现 {} 个可能的教师姓名: {}", extractedTeachers.size(), extractedTeachers);

            // 为每个提取到的教师创建记录
            Set<String> processedTeachers = new HashSet<>();
            for (String teacherName : extractedTeachers) {
                backupSingleTeacher(teacherName, "", processedTeachers);
            }

            log.info("强制提取教师完成，实际创建/更新 {} 个教师记录", processedTeachers.size());

        } catch (Exception e) {
            log.error("强制提取教师失败", e);
        }
    }

    /**
     * 基于三段式格式的教师提取（用于强制提取）
     * 专门从第二个位置提取教师姓名
     */
    private void extractTeacherWithMinimalValidation(String text, Set<String> extractedTeachers) {
        if (StrUtil.isBlank(text)) {
            return;
        }

        // 按空格分割，查找第二个位置的教师姓名
        String[] parts = text.trim().split("\\s+");

        // 如果有至少2个部分，第二个部分可能是教师姓名
        if (parts.length >= 2) {
            String teacherCandidate = parts[1].trim();
            String cleanedName = cleanTeacherName(teacherCandidate);

            // 基于位置的最小验证
            if (StrUtil.isNotBlank(cleanedName) &&
                cleanedName.length() >= 1 &&
                cleanedName.length() <= 5 &&
                !cleanedName.matches(".*\\d.*") && // 不包含数字
                !cleanedName.matches(".*[a-zA-Z].*") && // 不包含英文
                !isObviouslyNotName(cleanedName)) { // 不是明显的非姓名词汇

                extractedTeachers.add(cleanedName);
                log.debug("基于位置提取到可能的教师: {} (原始: {}, 位置: 第2位)", cleanedName, teacherCandidate);
            }
        }
    }

    /**
     * 检查是否明显不是姓名
     */
    private boolean isObviouslyNotName(String text) {
        String[] obviouslyNotNames = {
            "语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理", "政治",
            "计算机", "体育", "美术", "音乐", "茶艺", "电工", "机械", "汽修",
            "教室", "机房", "实训", "操场", "课程", "班级", "学期", "学年",
            "周一", "周二", "周三", "周四", "周五", "上午", "下午", "晚上",
            "第一", "第二", "第三", "第四", "第五", "早读", "晚修", "自习"
        };

        for (String notName : obviouslyNotNames) {
            if (text.equals(notName) || text.contains(notName)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 清理无效的课程数据（管理员手动调用）
     * 删除那些明显不是有效课程名称的记录
     */
    public void cleanInvalidCourseData() {
        log.info("开始清理无效课程数据...");

        try {
            // 获取所有课程数据
            cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO pageReq =
                    new cn.iocoder.yudao.module.ao.controller.admin.course.vo.CoursePageReqVO();
            pageReq.setPageSize(1000); // 获取大量数据

            List<cn.iocoder.yudao.module.ao.dal.dataobject.course.CourseDO> allCourses =
                    courseService.getCoursePage(pageReq).getList();

            List<Long> invalidCourseIds = new ArrayList<>();

            for (cn.iocoder.yudao.module.ao.dal.dataobject.course.CourseDO course : allCourses) {
                String courseName = course.getCourseName();

                // 检查是否为无效的课程名称
                if (!isValidSubject(courseName)) {
                    invalidCourseIds.add(course.getCourseId());
                    log.info("标记删除无效课程: {} (ID: {})", courseName, course.getCourseId());
                }
            }

            // 批量删除无效课程数据
            if (!invalidCourseIds.isEmpty()) {
                courseService.deleteCourseListByIds(invalidCourseIds);
                log.info("成功删除 {} 条无效课程数据", invalidCourseIds.size());
            } else {
                log.info("没有发现无效课程数据");
            }

        } catch (Exception e) {
            log.error("清理无效课程数据失败", e);
        }
    }

    /**
     * 测试新的结构化课程信息提取（临时测试方法）
     */
    public void testStructuredCourseExtraction() {
        log.info("=== 开始测试结构化课程信息提取 ===");

        // 基于实际课程表格式的测试用例
        String[] testCourses = {
            "语文 张老师 B215",           // 标准格式
            "数学 李老师 A301",           // 标准格式
            "计算机 王老师 机房1",         // 包含特殊场地
            "体育 陈老师 操场",           // 体育场地
            "茶艺 刘老师 茶艺室",         // 专业教室
            "电工 赵老师 电工室",         // 实训室
            "语文 张老师",               // 缺少地点
            "数学",                     // 只有科目
            "无效科目 张老师 B215",       // 无效科目
            "语文 无效姓名 B215"          // 无效教师姓名
        };

        Set<String> processedTeachers = new HashSet<>();
        Set<String> processedLocations = new HashSet<>();
        Set<String> processedCourses = new HashSet<>();

        for (String course : testCourses) {
            log.info("测试课程信息: {}", course);
            extractTeacherFromStructuredCourse(course, processedTeachers);
            extractLocationFromStructuredCourse(course, processedLocations);
            extractCourseFromStructuredCourse(course, processedCourses);
        }

        log.info("提取到的教师: {}", processedTeachers);
        log.info("提取到的场地: {}", processedLocations);
        log.info("提取到的课程: {}", processedCourses);
        log.info("=== 结构化课程信息提取测试完成 ===");
    }

    /**
     * 测试基于位置的教师提取逻辑（临时测试方法）
     */
    public void testPositionBasedTeacherExtraction() {
        log.info("=== 开始测试基于位置的教师提取逻辑 ===");

        // 基于实际课程表格式的测试用例（包含稀有姓氏）
        String[] testCourses = {
            "语文 欧阳老师 B215",         // 复姓
            "数学 司马教师 A301",         // 复姓
            "英语 上官讲师 C102",         // 复姓
            "计算机 单老师 机房1",        // 单字姓
            "体育 么老师 操场",           // 稀有姓氏
            "茶艺 仉老师 茶艺室",         // 稀有姓氏
            "电工 亓老师 电工室",         // 稀有姓氏
            "美术 郇老师 美术室",         // 稀有姓氏
            "音乐 厍老师 音乐室",         // 稀有姓氏
            "烹饪 乜老师 烹饪室",         // 稀有姓氏
            "护理 佴老师 实训室",         // 稀有姓氏
            "会计 郄老师 B301",           // 稀有姓氏
            "语文 张 B215",               // 无职务后缀
            "数学 李 A301",               // 无职务后缀
            "英语 王教授 C102",           // 教授职务
            "物理 赵讲师 D201",           // 讲师职务
        };

        Set<String> processedTeachers = new HashSet<>();

        for (String course : testCourses) {
            log.info("测试课程信息: {}", course);
            extractTeacherFromStructuredCourse(course, processedTeachers);
        }

        log.info("基于位置提取到的教师: {}", processedTeachers);
        log.info("=== 基于位置的教师提取逻辑测试完成 ===");
    }

    /**
     * 扫描现有课程表数据，发现并补充遗漏的科目和教师
     */
    public void scanAndSupplementMissingData() {
        log.info("=== 开始扫描现有课程表数据，发现遗漏的科目和教师 ===");

        try {
            // 获取所有中高技班级课数据
            List<VocationalClassScheduleDO> allSchedules = vocationalClassScheduleMapper.selectList(new LambdaQueryWrapperX<>());

            Set<String> discoveredSubjects = new HashSet<>();
            Set<String> discoveredTeachers = new HashSet<>();

            for (VocationalClassScheduleDO schedule : allSchedules) {
                // 扫描各天课程
                scanCourseForMissingData(schedule.getMondayCourse(), discoveredSubjects, discoveredTeachers);
                scanCourseForMissingData(schedule.getTuesdayCourse(), discoveredSubjects, discoveredTeachers);
                scanCourseForMissingData(schedule.getWednesdayCourse(), discoveredSubjects, discoveredTeachers);
                scanCourseForMissingData(schedule.getThursdayCourse(), discoveredSubjects, discoveredTeachers);
                scanCourseForMissingData(schedule.getFridayCourse(), discoveredSubjects, discoveredTeachers);

                // 扫描班主任
                if (StrUtil.isNotBlank(schedule.getClassTeacher())) {
                    String cleanedTeacher = cleanTeacherName(schedule.getClassTeacher());
                    if (StrUtil.isNotBlank(cleanedTeacher)) {
                        discoveredTeachers.add(cleanedTeacher);
                    }
                }
            }

            log.info("发现的科目: {}", discoveredSubjects);
            log.info("发现的教师: {}", discoveredTeachers);

            // 补充遗漏的科目
            Set<String> processedCourses = new HashSet<>();
            for (String subject : discoveredSubjects) {
                backupSingleCourse(subject, processedCourses);
            }

            // 补充遗漏的教师
            Set<String> processedTeachers = new HashSet<>();
            for (String teacher : discoveredTeachers) {
                backupSingleTeacher(teacher, "", processedTeachers);
            }

            log.info("扫描完成，补充了 {} 个科目，{} 个教师", processedCourses.size(), processedTeachers.size());

        } catch (Exception e) {
            log.error("扫描补充数据失败", e);
        }
    }

    /**
     * 扫描单个课程信息，发现遗漏的科目和教师
     */
    private void scanCourseForMissingData(String courseInfo, Set<String> discoveredSubjects, Set<String> discoveredTeachers) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割课程信息
        String[] parts = courseInfo.trim().split("\\s+");

        if (parts.length >= 2) {
            String subjectCandidate = parts[0].trim();
            String teacherCandidate = parts[1].trim();

            // 发现可能的科目（更宽松的条件）
            if (isPotentialSubject(subjectCandidate)) {
                discoveredSubjects.add(subjectCandidate);
                log.debug("发现可能的科目: {}", subjectCandidate);
            }

            // 发现可能的教师
            String cleanedTeacher = cleanTeacherName(teacherCandidate);
            if (StrUtil.isNotBlank(cleanedTeacher) && isPotentialTeacher(cleanedTeacher)) {
                discoveredTeachers.add(cleanedTeacher);
                log.debug("发现可能的教师: {}", cleanedTeacher);
            }
        }
    }

    /**
     * 判断是否为可能的科目（比isValidSubject更宽松）
     */
    private boolean isPotentialSubject(String text) {
        if (StrUtil.isBlank(text) || text.length() < 2 || text.length() > 15) {
            return false;
        }

        // 如果已经是有效科目，直接返回true
        if (isValidSubject(text)) {
            return true;
        }

        // 排除明显不是科目的内容
        String[] notSubjects = {
            "老师", "教师", "讲师", "教授", "班主任", "任课",
            "教室", "机房", "实训室", "操场", "体育馆",
            "上午", "下午", "晚上", "中午", "早上",
            "周一", "周二", "周三", "周四", "周五",
            "第一", "第二", "第三", "第四", "第五"
        };

        for (String notSubject : notSubjects) {
            if (text.contains(notSubject)) {
                return false;
            }
        }

        // 如果包含数字和字母，可能是教室编号，不是科目
        if (text.matches(".*[A-Za-z]\\d+.*") || text.matches(".*\\d+[A-Za-z].*")) {
            return false;
        }

        // 其他情况认为可能是科目
        return true;
    }

    /**
     * 判断是否为可能的教师（比现有验证更宽松）
     */
    private boolean isPotentialTeacher(String text) {
        if (StrUtil.isBlank(text) || text.length() < 1 || text.length() > 4) {
            return false;
        }

        // 排除明显不是姓名的内容
        return !isObviouslyNotName(text) &&
               !text.matches(".*\\d.*") &&
               !text.matches(".*[a-zA-Z].*");
    }

    /**
     * 基于位置的智能课程信息提取
     * 针对格式：课程名称 教师名称 上课地点
     */
    public void extractCoursesByPosition() {
        log.info("=== 开始基于位置的智能课程信息提取 ===");

        try {
            // 获取所有中高技班级课数据
            List<VocationalClassScheduleDO> allSchedules = vocationalClassScheduleMapper.selectList(new LambdaQueryWrapperX<>());

            Set<String> processedCourses = new HashSet<>();
            Set<String> processedTeachers = new HashSet<>();
            Set<String> processedLocations = new HashSet<>();

            for (VocationalClassScheduleDO schedule : allSchedules) {
                // 提取各天课程信息
                extractFromCourseInfo(schedule.getMondayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getTuesdayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getWednesdayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getThursdayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getFridayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getSaturdayCourse(), processedCourses, processedTeachers, processedLocations);
                extractFromCourseInfo(schedule.getSundayCourse(), processedCourses, processedTeachers, processedLocations);
            }

            log.info("基于位置提取完成 - 课程: {}, 教师: {}, 地点: {}",
                processedCourses.size(), processedTeachers.size(), processedLocations.size());
            log.info("提取的课程: {}", processedCourses);
            log.info("提取的教师: {}", processedTeachers);
            log.info("提取的地点: {}", processedLocations);

        } catch (Exception e) {
            log.error("基于位置的课程信息提取失败", e);
        }
    }

    /**
     * 从单个课程信息中提取课程、教师、地点
     * 格式：课程名称 教师名称 上课地点
     */
    private void extractFromCourseInfo(String courseInfo, Set<String> processedCourses,
                                     Set<String> processedTeachers, Set<String> processedLocations) {
        if (StrUtil.isBlank(courseInfo)) {
            return;
        }

        // 按空格分割，期望得到3部分：课程名称 教师名称 上课地点
        String[] parts = courseInfo.trim().split("\\s+");

        if (parts.length >= 3) {
            String courseName = parts[0].trim();
            String teacherName = parts[1].trim();
            String location = parts[2].trim();

            log.debug("解析课程信息: {} -> 课程:{}, 教师:{}, 地点:{}", courseInfo, courseName, teacherName, location);

            // 基于位置的课程名称验证（更宽松）
            if (isValidCourseByPosition(courseName)) {
                backupSingleCourse(courseName, processedCourses);
                log.debug("基于位置提取课程: {}", courseName);
            }

            // 基于位置的教师名称验证
            String cleanedTeacher = cleanTeacherName(teacherName);
            if (StrUtil.isNotBlank(cleanedTeacher) && isValidTeacherNameByPosition(cleanedTeacher)) {
                backupSingleTeacher(cleanedTeacher, courseName, processedTeachers);
                log.debug("基于位置提取教师: {} (课程: {})", cleanedTeacher, courseName);
            }

            // 基于位置的地点验证
            if (isValidLocationByPosition(location)) {
                String locationType = determineLocationType(location);
                backupSingleLocation(location, locationType, processedLocations);
                log.debug("基于位置提取地点: {} (类型: {})", location, locationType);
            }
        } else if (parts.length == 2) {
            // 可能是：课程名称 教师名称（没有地点）
            String courseName = parts[0].trim();
            String teacherName = parts[1].trim();

            if (isValidCourseByPosition(courseName)) {
                backupSingleCourse(courseName, processedCourses);
                log.debug("基于位置提取课程(无地点): {}", courseName);
            }

            String cleanedTeacher = cleanTeacherName(teacherName);
            if (StrUtil.isNotBlank(cleanedTeacher) && isValidTeacherNameByPosition(cleanedTeacher)) {
                backupSingleTeacher(cleanedTeacher, courseName, processedTeachers);
                log.debug("基于位置提取教师(无地点): {} (课程: {})", cleanedTeacher, courseName);
            }
        }
    }

    /**
     * 基于位置的课程名称验证（比isValidSubject更宽松）
     */
    private boolean isValidCourseByPosition(String courseName) {
        if (StrUtil.isBlank(courseName) || courseName.length() < 1 || courseName.length() > 20) {
            return false;
        }

        log.debug("课程名称验证: {}", courseName);

        // 如果已经是有效科目，直接返回true
        if (isValidSubject(courseName)) {
            log.debug("通过isValidSubject验证: {}", courseName);
            return true;
        }

        // 排除时间格式
        if (courseName.matches("^\\d{1,2}:\\d{2}.*") || courseName.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
            log.debug("排除时间格式: {}", courseName);
            return false;
        }

        // 排除明显不是课程的内容
        String[] notCourses = {
            "老师", "教师", "讲师", "教授", "班主任",
            "教室", "机房", "实训室", "操场", "体育馆", "宿舍", "食堂",
            "上午", "下午", "晚上", "中午", "早上",
            "周一", "周二", "周三", "周四", "周五", "周六", "周日"
        };

        for (String notCourse : notCourses) {
            if (courseName.contains(notCourse)) {
                log.debug("排除非课程内容: {} (包含: {})", courseName, notCourse);
                return false;
            }
        }

        // 如果包含数字和字母组合，可能是教室编号，不是课程
        if (courseName.matches(".*[A-Za-z]\\d+.*") || courseName.matches(".*\\d+[A-Za-z].*")) {
            log.debug("排除教室编号格式: {}", courseName);
            return false;
        }

        // 基于位置的宽松验证：
        // 1. 如果是纯中文，很可能是课程名称
        if (courseName.matches("^[\\u4e00-\\u9fa5]+$")) {
            log.debug("通过纯中文验证: {}", courseName);
            return true;
        }

        // 2. 如果包含中文和常见课程特征，也认为是课程
        if (courseName.matches(".*[\\u4e00-\\u9fa5].*")) {
            // 包含常见的课程特征字符
            String[] courseIndicators = {
                "学", "课", "文", "理", "史", "艺", "技", "法", "经", "管",
                "语", "数", "英", "物", "化", "生", "政", "地", "体", "美",
                "论", "古", "诗", "词", "书", "画", "乐", "舞", "歌", "琴",
                "机", "模", "型", "力", "茶", "飞", "计算", "应用", "规划"
            };

            for (String indicator : courseIndicators) {
                if (courseName.contains(indicator)) {
                    log.debug("通过课程特征验证: {} (包含: {})", courseName, indicator);
                    return true;
                }
            }
        }

        // 3. 对于一段式格式，如果长度合适且不包含明显的非课程标识，也接受
        if (courseName.length() >= 2 && courseName.length() <= 10) {
            // 不包含数字的纯文本，很可能是课程名称
            if (!courseName.matches(".*\\d.*")) {
                log.debug("通过一段式宽松验证: {} (长度: {}, 无数字)", courseName, courseName.length());
                return true;
            }
        }

        log.debug("课程名称验证失败: {}", courseName);
        return false;
    }

    /**
     * 基于位置的地点验证
     */
    private boolean isValidLocationByPosition(String location) {
        if (StrUtil.isBlank(location) || location.length() < 1 || location.length() > 20) {
            return false;
        }

        // 排除明显不是地点的内容
        String[] notLocations = {
            "老师", "教师", "讲师", "教授",
            "上午", "下午", "晚上", "中午", "早上",
            "周一", "周二", "周三", "周四", "周五"
        };

        for (String notLocation : notLocations) {
            if (location.contains(notLocation)) {
                return false;
            }
        }

        // 基于位置，第三个位置的内容更可能是地点
        return true;
    }

    /**
     * 测试新的格式识别逻辑
     */
    public void testNewFormatRecognition() {
        log.info("=== 开始测试新的格式识别逻辑 ===");

        try {
            // 测试各种格式的课程信息
            String[] testCourses = {
                // 一段式：课程名称
                "飞机模型",
                "论语",
                "学习力",
                "茶艺",
                "体育",

                // 两段式：课程名称 教师名称
                "语文 张老师",
                "数学 李老师",
                "英语 王老师",
                "飞机模型 林洁玫",
                "论语 张老师",

                // 三段式：课程名称 教师名称 地点
                "语文 张老师 B215",
                "数学 李老师 A301",
                "茶艺 刘老师 茶艺室",
                "体育 陈老师 操场",
                "计算机 王老师 机房1",

                // 四段式：课程第一部分 课程第二部分 教师名称 地点
                "飞机 模型 林洁玫 面试实训室",
                "茶 艺 刘老师 茶艺室",
                "计算机 应用 王老师 机房1",
                "职业 规划 吴老师 会议室",
                "学习 力 陈老师 A101",

                // 多段式：地点名称包含空格
                "飞机 模型 林洁玫 博学楼 机房7",
                "茶 艺 刘老师 大 舞蹈室"
            };

            Set<String> processedCourses = new HashSet<>();
            Set<String> processedTeachers = new HashSet<>();
            Set<String> processedLocations = new HashSet<>();

            for (String course : testCourses) {
                log.info("=== 测试课程信息: {} ===", course);

                // 测试教师提取
                extractTeacherFromStructuredCourse(course, processedTeachers);

                // 测试地点提取
                extractLocationFromStructuredCourse(course, processedLocations);

                // 测试课程提取
                extractCourseFromStructuredCourse(course, processedCourses);

                log.info("");
            }

            log.info("=== 测试结果汇总 ===");
            log.info("提取到的课程: {}", processedCourses);
            log.info("提取到的教师: {}", processedTeachers);
            log.info("提取到的地点: {}", processedLocations);

            log.info("课程总数: {}", processedCourses.size());
            log.info("教师总数: {}", processedTeachers.size());
            log.info("地点总数: {}", processedLocations.size());

        } catch (Exception e) {
            log.error("测试新的格式识别逻辑失败", e);
        }

        log.info("=== 新的格式识别逻辑测试完成 ===");
    }

    /**
     * 测试一段式课程验证逻辑
     */
    public void testSingleSegmentCourseValidation() {
        log.info("=== 开始测试一段式课程验证逻辑 ===");

        try {
            // 测试各种一段式课程名称
            String[] testCourses = {
                // 应该通过验证的课程
                "飞机模型",     // 包含"机"和"模"
                "论语",         // 纯中文
                "学习力",       // 包含"学"和"力"
                "茶艺",         // 包含"茶"和"艺"
                "体育",         // 包含"体"
                "语文",         // 包含"语"和"文"
                "数学",         // 包含"数"和"学"
                "英语",         // 包含"英"和"语"
                "计算机",       // 包含"计算"和"机"
                "职业规划",     // 包含"规划"
                "美术",         // 包含"美"
                "音乐",         // 包含"乐"

                // 应该被过滤的内容
                "张老师",       // 包含"老师"
                "A101",         // 教室编号
                "机房1",        // 包含"机房"
                "07:40-08:20",  // 时间格式
                "2023-12-01",   // 日期格式
                "上午",         // 时间词
                "周一"          // 星期词
            };

            Set<String> validCourses = new HashSet<>();
            Set<String> invalidCourses = new HashSet<>();

            for (String course : testCourses) {
                log.info("--- 测试课程: {} ---", course);

                boolean isValid = isValidCourseByPosition(course);

                if (isValid) {
                    validCourses.add(course);
                    log.info("✅ 验证通过: {}", course);
                } else {
                    invalidCourses.add(course);
                    log.info("❌ 验证失败: {}", course);
                }

                log.info("");
            }

            log.info("=== 验证结果汇总 ===");
            log.info("通过验证的课程: {}", validCourses);
            log.info("未通过验证的课程: {}", invalidCourses);

            log.info("通过验证数量: {}", validCourses.size());
            log.info("未通过验证数量: {}", invalidCourses.size());

            // 测试实际备份功能
            log.info("=== 测试实际备份功能 ===");
            Set<String> processedCourses = new HashSet<>();

            for (String course : validCourses) {
                try {
                    extractAndBackupCourse(course, processedCourses);
                    log.info("✅ 备份成功: {}", course);
                } catch (Exception e) {
                    log.error("❌ 备份失败: {} - {}", course, e.getMessage());
                }
            }

            log.info("成功备份的课程: {}", processedCourses);

        } catch (Exception e) {
            log.error("测试一段式课程验证逻辑失败", e);
        }

        log.info("=== 一段式课程验证逻辑测试完成 ===");
    }

    /**
     * 检查是否有周六或周日的课程数据
     */
    public boolean hasWeekendCourseData() {
        try {
            // 查询是否存在周六或周日有课程数据的记录
            LambdaQueryWrapperX<VocationalClassScheduleDO> queryWrapper = new LambdaQueryWrapperX<>();
            queryWrapper.and(wrapper ->
                wrapper.isNotNull(VocationalClassScheduleDO::getSaturdayCourse)
                       .ne(VocationalClassScheduleDO::getSaturdayCourse, "")
                       .ne(VocationalClassScheduleDO::getSaturdayCourse, " ")
                       .or()
                       .isNotNull(VocationalClassScheduleDO::getSundayCourse)
                       .ne(VocationalClassScheduleDO::getSundayCourse, "")
                       .ne(VocationalClassScheduleDO::getSundayCourse, " ")
            );
            queryWrapper.last("LIMIT 1"); // 只需要检查是否存在，不需要查询所有数据

            List<VocationalClassScheduleDO> result = vocationalClassScheduleMapper.selectList(queryWrapper);
            boolean hasWeekendData = !result.isEmpty();

            log.info("检查周末课程数据: {}, 查询结果数量: {}", hasWeekendData ? "存在" : "不存在", result.size());

            // 如果有数据，打印一些示例数据用于调试
            if (hasWeekendData && !result.isEmpty()) {
                VocationalClassScheduleDO sample = result.get(0);
                log.info("示例周末数据 - 周六: [{}], 周日: [{}]",
                    sample.getSaturdayCourse(), sample.getSundayCourse());
            }

            return hasWeekendData;

        } catch (Exception e) {
            log.error("检查周末课程数据失败", e);
            return false; // 出错时默认不显示周末字段
        }
    }

}